/******************************************************************************

    Copyright (C) 2006 Alessio Stalla (alessiostalla@gmail.com)

    This file is part of PrettyProlog.

    PrettyProlog is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    PrettyProlog is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with PrettyProlog; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    See the file COPYING for further details.

 *****************************************************************************/

package prettyprolog.engine.syspreds;

import java.util.List;
import java.util.Vector;

import prettyprolog.datatypes.Cons;
import prettyprolog.datatypes.Nil;
import prettyprolog.datatypes.Real;
import prettyprolog.datatypes.Substitution;
import prettyprolog.datatypes.Symbol;
import prettyprolog.datatypes.Term;
import prettyprolog.datatypes.Variable;
import prettyprolog.engine.Domain;
import prettyprolog.engine.Engine;

/**
 * Implements the dom/3 system predicate.
 */
public class Domini extends SystemPredicate {

	private static final Domini inst = new Domini();
	
	private Domini() {
		super(3);
	}

	/** Returns the only instance of this system predicate. */
	public static Domini getInstance() {
		return inst;
	}

	public SyspredEffect doCall(Engine en, Substitution subst, List<Term> args)	throws SyspredException {
		
		Vector Adom = new Vector();
		Vector Bdom = new Vector();
		Vector varList = subst.getVariabili();
		Vector varSubList = new Vector();
		
//		en.getIOManager().output("getVariabili: "+varList.toString()+"\n");
		for(int i=0; i < varList.size(); i++) {
			Term t1 = (Term) varList.get(i);
			Term t2 = subst.getSubst((Term) varList.get(i));
			varSubList.add(t2);
		}
//		en.getIOManager().output("varSubList: "+varSubList.toString()+"\n");
		SyspredEffect risp = null;

		for(int j=0; j < 2; j++) {			// se i=0 definisco ArcDomain, se i=1 definisco BoundDomain
			
			boolean flag = true;
			
			if(j==0) {
				Adom = en.getStack().get(0).getArcDomain();
				en.recoveDomain(Adom);
			} else if (j==1) {
				Bdom = en.getStack().get(0).getBoundDomain();
				en.recoveDomain(Bdom);
			}
			
			if ((args.get(1) instanceof Real) && (args.get(2) instanceof Real)) {

				int n = (int) ((Real) args.get(1)).getValue();
				int m = (int) ((Real) args.get(2)).getValue();

				if (args.get(0) instanceof Cons) {
					
					Vector tempo = new Vector();
					List<Term> tempo1 = new Vector();
					getElem((Cons) args.get(0), tempo1, en, subst);
					getVar((Cons) args.get(0), tempo, en, subst);
//					en.getIOManager().output("tempo1 : "+tempo1.toString()+"\n");
//					en.getIOManager().output("tempo : "+tempo.toString()+"\n");
					
					for(Term t : tempo1) {
						
						//						en.getIOManager().output("Substitution: "+subst.toString()+"\n");
						
						if ((t instanceof Variable) ) {
							
							Variable temp = subst.getVar((Term) t);
							Term subtemp = subst.getSubst(temp);
							
//							en.getIOManager().output("Term t: "+t.toString()+"\n");
//							en.getIOManager().output("Term temp: "+temp.toString()+"\n");
//							en.getIOManager().output("Term subst t: "+subst.getSubst(t)+"\n");
							
							if(varSubList.contains(t) || !(varSubList.contains(t))) {
								
//								en.getIOManager().output("Term subst temp: "+subst.getSubst(temp)+"\n");
								
								if(!Domain.isDomain(temp)) {
									
//									en.getIOManager().output("1: "+"\n");
									Domain.insertDomain(temp);
									Domain.setDomain(temp, n, m);
									if(!varSubList.contains(t)) {
										Domain.insertDomain(((Variable) t));
										Domain.setDomain(((Variable) t), n, m);
									}
//									Domain.insertDomain(((Variable) t));
//									Domain.setDomain(((Variable) t), n, m);
									flag = flag && true;
//									risp = new SyspredEffect(false, subst);
								} else {
									Domain.newdomain(en, temp, subst, n, m);	// ne interseco i domini
//									Domain.newdomain(en, (Variable) t, subst, n, m);	// ne interseco i domini
									flag = flag && true;
//									risp = new SyspredEffect(false, subst);
									
								}
								if(flag) {
									if(temp.toString().startsWith("_")) {
										if(j==0) {
											en.getIOManager().output1(" "+temp.getSubst().toString()+" in ["+Domain.min(temp)+" .. "+Domain.max(temp)+"]"+"\n"+"\n");
										} else {
											en.getIOManager().output2(" "+temp.getSubst().toString()+" in ["+Domain.min(temp)+" .. "+Domain.max(temp)+"]"+"\n"+"\n");
										}
									} else {
										if(j==0) {
											en.getIOManager().output1(" "+temp.toString()+" in ["+Domain.min(temp)+" .. "+Domain.max(temp)+"]"+"\n"+"\n");
										} else {
											en.getIOManager().output2(" "+temp.toString()+" in ["+Domain.min(temp)+" .. "+Domain.max(temp)+"]"+"\n"+"\n");
										}
									}
								}
								if(j==0) {
									Adom = en.saveDomain();
									en.getStack().get(0).setArcDomain(Adom);
								} else if (j==1) {
									Bdom = en.saveDomain();
									en.getStack().get(0).setBoundDomain(Bdom);
								}
								
//								risp = new SyspredEffect(false, subst);
								
							}
							
						} else if (t instanceof Real) {
							
							int a = (int) ((Real) t).getValue();
							Term subtemp = subst.getSubst(t);
//							en.getIOManager().output("Term t: "+t.toString()+"\n");
//							en.getIOManager().output("Term subtemp: "+subtemp+"\n");
//							en.getIOManager().output("Real "+a+"\n");
							if((a>=n && a <= m) || (a>=m && a <= n)) {
								flag = flag && true;
//								risp = new SyspredEffect(false, subst);
//								en.getIOManager().output("Real1 "+a+"\n");
							} else {
								flag = flag && false;
//								risp = new SyspredEffect(true, subst);
								if(j==0) {
									en.getIOManager().output("   "+a+" not in "+" ["+n+" .. "+m+"]"+"\n"+"\n");
								}
							}
							
						}
					}
				} else {
					if(j==0) {
						en.getIOManager().error("Sintassi:   domain([ListaVar], int, int) "+"\n");
					}
					flag = flag && false;
//					risp = new SyspredEffect(true, subst);
				}
			} else if (args.get(1) instanceof Cons && args.get(0) instanceof Cons) {
				Vector tempo = new Vector();
//				getElem((Cons) args.get(1), tempo);
				en.getIOManager().output(tempo.toString()+"\n");
				if (args.get(0) instanceof Variable) {
					Variable temp = (Variable) args.get(0);
					if(!Domain.isDomain(temp)) {
						Domain.insertDomain((Variable) temp);
						Domain.updateDomain((Variable) temp, tempo);
					} else {
						Domain.updateDomain((Variable) temp, tempo);
					}
				}
				flag = flag && true;
//				risp = new SyspredEffect(false, subst);
			} else
				flag = flag && false;
//				risp = new SyspredEffect(true, subst);
			if(flag) {
				risp = new SyspredEffect(false, subst);
			} else {
				risp = new SyspredEffect(true, subst);
			}
		}
		return risp;
	}
	
	public Vector getVar(Cons c, Vector temp, Engine en, Substitution sub) {
		Term tempo = c.car();
		if(tempo instanceof Real) {
			int a = (int) ((Real) tempo).getValue();
//			temp.add(a);
			if(c.cdr() != Nil.NIL) {
				getVar((Cons) c.cdr(), temp, en, sub);
			}
		} else if(tempo instanceof Variable) {
			temp.add(tempo);
			if(c.cdr() != Nil.NIL) {
				getVar((Cons) c.cdr(), temp, en, sub);
			}
		} else if(tempo instanceof Cons) {
			temp.add(tempo);
		}
		return temp;
	}
	
	public List<Term> getElem(Cons c, List<Term> temp, Engine en, Substitution sub) {
		Term tempo = c.car();
		if(tempo instanceof Real) {
//			int a = (int) ((Real) tempo).getValue();
			temp.add(tempo);
			if(c.cdr() != Nil.NIL) {
				getElem((Cons) c.cdr(), temp, en, sub);
			}
		} else if(tempo instanceof Variable) {
			temp.add(tempo);
			if(c.cdr() != Nil.NIL) {
				getElem((Cons) c.cdr(), temp, en, sub);
			}
		} else if(tempo instanceof Cons) {
			temp.add(tempo);
		}
		return temp;
	}
	
	public boolean allVar (Vector vec) {
		boolean risp = true;
		for(int y=0; y < vec.size(); y++) {
			if(!(vec.get(y) instanceof Variable)) {
				risp = false;
			}
		}
		return risp;
	}

	public boolean isBacktrackable() {
		return false;
	}

	public String toString() {
		return super.toString() + " (domain/3)";
	}
}